import {ElMessage} from "element-plus";
import axios from "axios";

const instance = axios.create({
    baseURL: '', // 明确不带默认baseURL
})

//存储在localStorage或者sessionStorage中的键名
const authItemName = "access_token";


/**
 * 默认请求失败处理函数
 * @param message   错误提示信息
 * @param code  错误状态码
 * @param url   请求地址
 */
const defaultFailure = (message, code, url) => {
    console.warn(`请求地址：${url},状态码：${code},错误信息:${message}`)
    ElMessage.warning(message)
}

/**
 * 默认请求异常处理函数
 * @param err   异常对象
 */
const defaultError = (err) => {
    console.warn(err)
    ElMessage.error(`发生了一些错误，请联系管理员`)
}

/**
 * 读取access_token
 * @returns {*|null}    token字符串或者null
 */
function takeAccessToken() {
    const str = localStorage.getItem(authItemName) || sessionStorage.getItem(authItemName)
    if (!str) {
        return null
    }
    const authObj = JSON.parse(str)
    if (authObj.expire <= new Date()) {
        deleteAccessToken()
        ElMessage.warning(`登录状态已过期,请重新登录`)
        return null
    }

    return authObj.token
}

/**
 * 存储access_token到本地或会话中
 * @param token 登陆后返回的token
 * @param remember  是否记住我
 * @param expire    过期时间
 */
function storeAccessToken(token, remember, expire) {
    const authObj = {token: token, expire: expire}
    const strAuthObj = JSON.stringify(authObj)
    if (!remember) {
        sessionStorage.setItem(authItemName, strAuthObj)
    } else {
        localStorage.setItem(authItemName, strAuthObj)
    }
}

/**
 * 删除存储的access_token
 */
function deleteAccessToken() {
    localStorage.removeItem(authItemName)
    sessionStorage.removeItem(authItemName)
}

/**
 * 获取请求头中的Authorization字段
 * @returns {{Authorization: string}|{}}    包含Bearer token的headers，未登录返回空对象
 */
function accessHeader() {
    const token = takeAccessToken();
    return token ? {'Authorization': `Bearer ${takeAccessToken()}`} : {};
}

/**
 * 封装的POST请求方法
 * @param url   请求地址
 * @param data  POST请求体参数
 * @param header    请求头
 * @param success   成功回调
 * @param failure   失败回调
 * @param error 异常处理回调
 */
function internalPost(url, data, header, success, failure, error = defaultError) {
    instance.post(url, data, {headers: header}).then(({data}) => {  // 用 instance 代替 axios
        if (data.code === 200) {
            success(data.data)
        } else {
            failure(data.message, data.code, url)
        }
    }).catch(err => error(err))
}

/**
 * 封装的GET请求方法
 * @param url   请求地址
 * @param header    请求头
 * @param success   成功回调
 * @param failure   失败回调
 * @param error 异常处理回调
 */
function internalGet(url, header, success, failure, error = defaultError) {
    instance.get(url, {headers: header}).then(({data}) => {  // 用 instance 代替 axios
        if (data.code === 200) {
            success(data.data)
        } else {
            failure(data.message, data.code, url)
        }
    }).catch(err => error(err))
}

/**
 * 外部POST请求方法，自动加上Authorization头
 * @param url   请求地址
 * @param data  请求体数据
 * @param success   成功回调函数
 * @param failure   失败回调函数
 */
function post(url, data, success, failure = defaultFailure) {
    internalPost(url, data, accessHeader(), success, failure)
}

/**
 * 外部GET请求方式，自动加上Authorization头
 * @param url   请求地址
 * @param success   成功回调函数
 * @param failure   失败回调函数
 */
function get(url, success, failure = defaultFailure) {
    internalGet(url, accessHeader(), success, failure)
}

/**
 * 用户登录函数
 * 发送用户名、密码到后端接口进行验证，登录成功后保存token
 * @param username  用户名
 * @param password  密码
 * @param remember  是否记住我
 * @param success   成功回调
 * @param failure   失败回调
 */
function login(username, password, remember, success, failure = defaultFailure) {
    internalPost(
        '/api/auth/login',
        {
            username: username,
            password: password,
            remember: remember
        },
        {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        (data) => {
            storeAccessToken(data.token, remember, data.expire)
            ElMessage.success(`登陆成功,欢迎${data.username}进入本平台`)
            success(data)
        },
        failure
    )
}

/**
 * 用户登出函数
 * 像后端发送退出请求，清除access_token
 * @param success   成功回调函数
 * @param failure   失败回调函数
 */
function logout(success, failure = defaultFailure) {
    get('/api/auth/logout',
        () => {
            deleteAccessToken()
            ElMessage.success(`退出登录成功，欢迎再次使用`)
            success()
        },
        failure)
}

/**
 * 判断当前是否为未授权状态
 * @returns {boolean}   true表示未登录或已过期
 */
function unauthorized() {
    return !takeAccessToken()
}

instance.interceptors.request.use(config => {
    return config
})

//导出封装的请求和认证工具
export {login, logout, get, post, unauthorized}